package com.hahaliu.hahaliu.service.impl.comment;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hahaliu.hahaliu.common.exception.BaseException;
import com.hahaliu.hahaliu.common.utils.RedisUtil;
import com.hahaliu.hahaliu.mapper.CommentMapper;
import com.hahaliu.hahaliu.mapper.VideoMapper;
import com.hahaliu.hahaliu.pojo.entity.Comment;
import com.hahaliu.hahaliu.pojo.entity.CommentTree;
import com.hahaliu.hahaliu.pojo.entity.Video;
import com.hahaliu.hahaliu.service.comment.CommentService;
import com.hahaliu.hahaliu.service.user.UserService;
import com.hahaliu.hahaliu.service.video.VideoStatsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
* @author Liubuzhu
* @description 针对表【comment】的数据库操作Service实现
* @createDate 2024-10-20 14:33:46
*/
@Service
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment>
    implements CommentService{

	@Autowired
	private CommentMapper commentMapper;
	@Autowired
	private VideoStatsService videoStatsService;
	@Autowired
	private UserService userService;
	@Autowired
	private VideoMapper videoMapper;
	@Autowired
	private RedisUtil redisUtil;
	@Autowired
	@Qualifier("taskExecutor")
	private Executor taskExecutor;

	@Override
	public List<CommentTree> getCommentTreeByVid(Integer vid, Long offset, Integer type) {
		// 查询父级评论
		List<Comment> rootComments = getRootCommentsByVid(vid, offset, type);

		// 并行执行每个根级评论的子评论查询任务
		List<CommentTree> commentTreeList = rootComments.stream().parallel()
				.map(rootComment ->buildCommentTree(rootComment, 0L, 2L))
				.collect(Collectors.toList());

//        System.out.println(commentTreeList);

		return commentTreeList;
	}

	@Override
	@Transactional
	public CommentTree sendComment(Integer vid, Integer uid, Integer rootId, Integer parentId, Integer toUserId, String content) {
		if (content == null || content.isEmpty() || content.length() > 2000) return null;
		Comment comment = new Comment(
				null,
				vid,
				uid,
				rootId,
				parentId,
				toUserId,
				content, 0, 0, new Date(), 0, 0);

		commentMapper.insert(comment);
		// 更新视频评论 + 1
		videoStatsService.updateStats(comment.getVId(), "comment", true, 1);

		CommentTree commentTree = buildCommentTree(comment, 0L, -1L);

		try {
			CompletableFuture.runAsync(() -> {
				// 如果不是根级评论，则加入 redis 对应的 zset 中
				if (!rootId.equals(0)) {
					redisUtil.zset("comment_reply:" + rootId, comment.getCId());
				} else {
					redisUtil.zset("comment_video:"+ vid, comment.getCId());
				}
				// 表示被回复的用户收到的回复评论的 id 有序集合
				// 如果不是回复自己
//				if(!Objects.equals(comment.getToUserId(), comment.getUId())) {
//					redisUtil.zset("reply_zset:" + comment.getToUserId(), comment.getCId());
//					msgUnreadService.addOneUnread(comment.getToUserId(), "reply");
//
//					// netty 通知未读消息
//					Map<String, Object> map = new HashMap<>();
//					map.put("type", "接收");
//					Set<Channel> myChannels = IMServer.userChannel.get(comment.getToUserId());
//					if (myChannels != null) {
//						for (Channel channel: myChannels) {
//							channel.writeAndFlush(IMResponse.message("reply", map));
//						}
//					}
//				}
			}, taskExecutor);
		} catch (Exception e) {
			log.error("发送评论过程中出现一点差错");
			e.printStackTrace();
		}

		return commentTree;
	}

	/**
	 * 构建评论树
	 * @param comment 根评论
	 * @param start 子评论开始偏移量
	 * @param stop  子评论结束偏移量
	 * @return  单棵评论树
	 */
	private CommentTree buildCommentTree(Comment comment, Long start, Long stop) {
		CommentTree tree = new CommentTree();
		tree.setId(comment.getCId());
		tree.setVid(comment.getVId());
		tree.setRootId(comment.getRootId());
		tree.setParentId(comment.getParentId());
		tree.setContent(comment.getContent());
		tree.setCreateTime(comment.getCreateTime());
		tree.setLove(comment.getLove());
		tree.setBad(comment.getBad());

		tree.setUser(userService.getById(comment.getUId()));
		tree.setToUser(userService.getById(comment.getToUserId()));

		// 递归查询构建子评论树
		// 这里如果是根节点的评论，则查出他的子评论； 如果不是根节点评论，则不查，只填写 User 信息。
		if (comment.getRootId() == 0) {
			long count = redisUtil.zCard("comment_reply:" + comment.getCId());
			tree.setCount(count);

			List<Comment> childComments = getChildCommentsByRootId(comment.getCId(), comment.getVId(), start, stop);

			List<CommentTree> childTreeList = childComments.stream().parallel()
					.map(childComment -> buildCommentTree(childComment, start, stop))
					.collect(Collectors.toList());

			tree.setReplies(childTreeList);
		}

		return tree;
	}


	/**
	 * @param rootId 根级节点的评论 id, 即楼层 id
	 * @param vid 视频的 vid
	 * @return 1. 根据 redis 查找出回复该评论的子评论 id 列表
	 * 2. 根据 id 多线程查询出所有评论的详细信息
	 */
	@Override
	public List<Comment> getChildCommentsByRootId(Integer rootId, Integer vid, Long start, Long stop) {
		Set<Object> replyIds = redisUtil.zRange("comment_reply:" + rootId, start, stop);

		if (replyIds == null || replyIds.isEmpty()) return Collections.emptyList();

		QueryWrapper<Comment> wrapper = new QueryWrapper<>();
		wrapper.in("c_id", replyIds).ne("is_delete", 1);

		return commentMapper.selectList(wrapper);
	}

	@Override
	public List<Comment> getRootCommentsByVid(Integer vid, Long offset, Integer type) {
		Set<Object> rootIdsSet;
		if (type == 1) {
			// 按热度排序就不能用时间分数查偏移量了，要全部查出来，后续在MySQL筛选
			rootIdsSet = redisUtil.zReverange("comment_video:" + vid, 0L, -1L);
		} else {
			rootIdsSet = redisUtil.zReverange("comment_video:" + vid, offset, offset + 9L);
		}

		if (rootIdsSet == null || rootIdsSet.isEmpty()) return Collections.emptyList();

		QueryWrapper<Comment> wrapper = new QueryWrapper<>();
		wrapper.in("c_id", rootIdsSet).ne("is_delete", 1);
		if (type == 1) { // 热度
			wrapper.orderByDesc("(love - bad)").last("LIMIT 10 OFFSET " + offset);
		} else { // 时间
			wrapper.orderByDesc("create_time");
		}
		return commentMapper.selectList(wrapper);
	}

	/**
	 * 删除评论
	 * @param id    评论id
	 * @param uid   当前用户id
	 * @param isAdmin   是否是管理员
	 * @return  响应对象
	 */
	@Override
	@Transactional
	public void deleteComment(Integer id, Integer uid, boolean isAdmin) {
		QueryWrapper<Comment> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("c_id", id).ne("is_delete", 1);
		Comment comment = commentMapper.selectOne(queryWrapper);
		if (comment == null) {
			throw new BaseException("评论不存在");
		}

		// 判断该用户是否有权限删除这条评论
		Video video = videoMapper.selectById(comment.getVId());
		if (Objects.equals(comment.getUId(), uid) || isAdmin || Objects.equals(video.getUId(), uid)) {
			// 删除评论
			UpdateWrapper<Comment> commentWrapper = new UpdateWrapper<>();
			commentWrapper.eq("c_id", comment.getCId()).set("is_delete", 1);
			commentMapper.update(null, commentWrapper);

			/**
			 * 如果该评论是根节点评论，则删掉其所有回复。
			 * 如果不是根节点评论，则将他所在的 comment_reply(zset) 中的 comment_id 删掉
			 */
			if (Objects.equals(comment.getRootId(), 0)) {
				// 查询总共要减少多少评论数
				int count = Math.toIntExact(redisUtil.zCard("comment_reply:" + comment.getCId()));
				videoStatsService.updateStats(comment.getVId(), "comment", false, count + 1);
				redisUtil.zsetDelMember("comment_video:" + comment.getVId(), comment.getCId());
				redisUtil.delValue("comment_reply:" + comment.getCId());
			} else {
				videoStatsService.updateStats(comment.getVId(), "comment", false, 1);
				redisUtil.zsetDelMember("comment_reply:" + comment.getRootId(), comment.getCId());
			}
		} else {
			throw new BaseException("你无权删除该条评论");
		}
	}

}




